/******************************************************
*  Copyright(c) pages.ilovezxl.top
*  FileName :ostime_linux.c
*  Author : rivulet.dave
*  Description : 系统时间LINUX实现
*  Data : 2020-07-07
******************************************************/

/*********************************************************************
 * INCLUDES
 */

#include "stdrope.h"

#include "sallib.h"
#include "salagent.h"

#if (SAL_TIME == _ENABLE && SAL_PRODUCER == _SAL_LINUX_)

#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>

#include "ostime.h"
#include "ostime_linux.h"

/*********************************************************************
 *  EXTERNAL VARIABLES
 */

/*********************************************************************
 * MACROS
 */

#define NSEC_PER_SEC    1000000000

/*********************************************************************
 * TYPES
 */


/*********************************************************************
 * FUNCTIONS DECLARE
 */

static ostime_t osSysTimeGetByLinux(void);
static ostime_t osTickGetByLinux(void);
static ostime_t osTimeGetByLinux(void);
static int32_t osTimeSetByLinux(ostime_t ulTimeStamp);
static void osDateGetByLinux(OS_DATE_S *pstDate);
static ostime_t osDate2TimeStampByLinux(OS_DATE_S *pstDate);
static void osTimeStamp2DateByLinux(ostime_t ulTimeStamp, OS_DATE_S *pstDate);
static void osSleepByLinux(uint32_t uiTimesec);
static void osSleepusByLinux(uint32_t uiTimeus);

static inline void tsnorm(struct timespec *ts)
{
	while (ts->tv_nsec >= NSEC_PER_SEC) {
		ts->tv_nsec -= NSEC_PER_SEC;
		ts->tv_sec++;
	}
}

/*********************************************************************
 *  VARIABLES
 */

static OSTIME_BASE_S s_stOSTimeLinuxObj;

/*********************************************************************
 * FUNCTIONS
 */

/**
 *   @Description: 获取系统时间对象
 *   @param : NA
 *   @return: 对象地址
 */
void *osTimeLinuxObjNew(void)
{
    s_stOSTimeLinuxObj.pfOSSysTimeGet = osSysTimeGetByLinux;
    s_stOSTimeLinuxObj.pfOSTickGet = osTickGetByLinux;
    s_stOSTimeLinuxObj.pfOSTimeGet = osTimeGetByLinux;
    s_stOSTimeLinuxObj.pfOSTimeSet = osTimeSetByLinux;
    s_stOSTimeLinuxObj.pfOSDateGet = osDateGetByLinux;
    s_stOSTimeLinuxObj.pfOSDate2TimeStamp = osDate2TimeStampByLinux;
    s_stOSTimeLinuxObj.pfOSTimeStamp2Date = osTimeStamp2DateByLinux;
    s_stOSTimeLinuxObj.pfOSSleep = osSleepByLinux;
    s_stOSTimeLinuxObj.pfOSSleepus = osSleepusByLinux;
    return &s_stOSTimeLinuxObj;
}

static ostime_t osSysTimeGetByLinux(void)
{
    struct timespec times = {0, 0};
    ostime_t ulTimeus;

    clock_gettime(CLOCK_MONOTONIC, &times);
    ulTimeus = times.tv_sec * 1000 + times.tv_nsec / 1000000;
	
    return ulTimeus;
}

static ostime_t osTickGetByLinux(void)
{
    ostime_t ulTimeus;
    struct timeval tv;

    gettimeofday(&tv, NULL);
    ulTimeus = (ostime_t)tv.tv_sec * 1000000;
    ulTimeus += tv.tv_usec;
    return ulTimeus;
}

static ostime_t osTimeGetByLinux(void)
{
    ostime_t ulTimeMs;
    struct timeval tv;
    
    gettimeofday(&tv,NULL);
    ulTimeMs = (ostime_t)tv.tv_sec * 1000;
    ulTimeMs += tv.tv_usec / 1000;
    return ulTimeMs;
}

static int32_t osTimeSetByLinux(ostime_t ulTimeStamp)
{
    char pBuf[100] = { 0 };
    char pCMD[120] = { 0 };
    time_t uiTime = (time_t)ulTimeStamp;

    struct tm *pLocalTime = localtime(&uiTime);
    if (strftime(pBuf, sizeof(pBuf), "%F %H:%M:%S", pLocalTime) < 0)
    {
        return _ERR;
    }

    if (sprintf(pCMD, "%s \"%s\"", "date -s", pBuf) < 0)
    {
        return _ERR;
    }

    if (system(pCMD) != 0)
    {
        return _ERR;
    }

    // 关闭硬件时间同步，同步硬件时钟时，可能会导致CAN通信中断
    // linux系统有两个时钟：一个是由主板电池驱动的硬件时钟(Real Time Clock)，
    // 也叫做RTC或者叫CMOS时钟。当操作系统关机的时候，用这个来记录时间，
    // 但是对于运行的系统是不用这个时间的；
    // 另一个时间是系统时钟(System clock)也叫内核时钟或者软件时钟，
    // 是由软件根据时间中断来进行计数的，内核时钟在系统关机的情况下是不存在的，
    // 所以，当操作系统启动的时候，内核时钟是要读取RTC时间来进行时间同步。
    // 并且在系统关机的时候将系统时间写回RTC中进行同步。
#if 0
    if (system("hwclock  -w") != 0)
    {
        return _ERR;
    }
#endif

    return _OK;
}

static void osDateGetByLinux(OS_DATE_S *pstDate)
{
    struct timeval tv;
    time_t uiTimeSecond;
    struct tm *pstLocaltm = NULL;

    if (NULL == pstDate)
    {
        return;
    }

    gettimeofday(&tv,NULL);
    uiTimeSecond = tv.tv_sec;
    pstLocaltm = localtime(&uiTimeSecond);

    pstDate->usYear = pstLocaltm->tm_year + 1900;
    pstDate->ucMonth = pstLocaltm->tm_mon + 1;
    pstDate->ucDay = pstLocaltm->tm_mday;
    pstDate->ucHour = pstLocaltm->tm_hour;
    pstDate->ucMinute = pstLocaltm->tm_min;
    pstDate->ucSecond = pstLocaltm->tm_sec;
    pstDate->ucWeek = pstLocaltm->tm_wday + 1;
    pstDate->uiMilliSecond = tv.tv_usec / 1000;
    return;
}

static ostime_t osDate2TimeStampByLinux(OS_DATE_S *pstDate)
{
    struct tm stTmp;
    ostime_t ulTimeStamp;

    if (NULL == pstDate)
    {
        return 0;
    }

    stTmp.tm_year = pstDate->usYear - 1900;
    stTmp.tm_mon = pstDate->ucMonth - 1;
    stTmp.tm_mday = pstDate->ucDay;
    stTmp.tm_hour = pstDate->ucHour;
    stTmp.tm_min = pstDate->ucMinute;
    stTmp.tm_sec = pstDate->ucSecond;
    stTmp.tm_wday = pstDate->ucWeek - 1;

    ulTimeStamp = mktime(&stTmp);
    ulTimeStamp = ulTimeStamp * 1000 + pstDate->uiMilliSecond;

    return ulTimeStamp;
}

static void osTimeStamp2DateByLinux(ostime_t ulTimeStamp, OS_DATE_S *pstDate)
{
    time_t uiTimeSec = (time_t)(ulTimeStamp / 1000);
    struct tm *pstLocaltm = localtime((time_t *)&uiTimeSec);

    if (NULL == pstDate)
    {
        return;
    }

    pstDate->usYear = pstLocaltm->tm_year + 1900;
    pstDate->ucMonth = pstLocaltm->tm_mon + 1;
    pstDate->ucDay = pstLocaltm->tm_mday;
    pstDate->ucHour = pstLocaltm->tm_hour;
    pstDate->ucMinute = pstLocaltm->tm_min;
    pstDate->ucSecond = pstLocaltm->tm_sec;
    pstDate->ucWeek = pstLocaltm->tm_wday + 1;
    pstDate->uiMilliSecond = (uint32_t)(ulTimeStamp % 1000);
    return;
}

static void osSleepByLinux(uint32_t uiTimesec)
{
    struct timespec startTime;
    
    if (0 != clock_gettime(CLOCK_MONOTONIC, &startTime))
    {
        return;
    }
    
    startTime.tv_sec += uiTimesec;
    tsnorm(&startTime);
    clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &startTime, NULL);
}

static void osSleepusByLinux(uint32_t uiTimeus)
{
    struct timespec startTime;
    
    if (0 != clock_gettime(CLOCK_MONOTONIC, &startTime))
    {
        return;
    }

    uiTimeus = uiTimeus * 1000;
    startTime.tv_nsec += uiTimeus;
    tsnorm(&startTime);
    clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &startTime, NULL);
}

#endif  // SAL_TIME == _ENABLE && SAL_PRODUCER == _SAL_LINUX_

/************************************** The End Of File **************************************/



